package com.utils;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import java.time.Duration;
import java.util.LinkedHashMap;

/**
 * 执行:不带参不带返回值的方法，补全 lambda 表达式不支持的部分
 *
 * @author 谢长春 2018-10-11
 */
public interface IAutoTask {
    /**
     * 当开关 spring.app.autoTask.services.{beanName}.started 开关开启时， 服务启动之后立即时执行一次 started 方法， 触发 call 方法。
     * 一般用于启动时初始化数据，可覆写该方法，附加判断条件之后再触发 call 方法
     * <code>
     * \@Transactional(propagation = Propagation.NEVER) // 这里需要加上事务注解，避免内部循环处理，导致一个批次的数据异常，影响整个定时任务
     * public void started(final String date) {
     * log.info("定时任务逻辑");
     * }
     * </code>
     */
    default void started() {
        call(null);
    }

    /**
     * 执行定时任务逻辑，可用于失败之后指定日期重新执行
     * Objects.isNull(args) ? yyyyMMdd.now() : args;
     * Objects.isNull(args) ? Dates.now().format(yyyyMMdd) : args;
     *
     * <code>
     * \@Transactional(propagation = Propagation.NEVER) // 这里需要加上事务注解，避免内部循环处理，导致一个批次的数据异常，影响整个定时任务
     * public void call(final String args) {
     * log.info("定时任务逻辑");
     * }
     * </code>
     *
     * @param args {@link String} 定时任务参数，一般用于挂起指定日期定时任务
     */
    void call(final String args);

    /**
     * 定时任务配置
     */
    @Getter
    @Setter
    @ToString
    class AutoTask {
        /**
         * 定时任务总开关，是否启用服务内部的定时任务 true：是，false：否
         */
        private boolean enabled = false;
        /**
         * 定时任务列表
         */
        private LinkedHashMap<String, AutoTaskItem> services;
    }

    @Getter
    @Setter
    @ToString
    class AutoTaskItem {
        /**
         * 注册到 spring context 的 bean，必须实现 IAutoTask 接口
         * 参考： AutoTaskService.java
         */
        private String name;
        /**
         * 定时任务开关，  true：开，false：关
         */
        private boolean enabled = false;
        /**
         * 是否在启动时先执行一次
         */
        private boolean started = false;
        /**
         * 任务说明
         */
        private String comment = "";
        /**
         * 执行时间表达式
         * >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
         * >> 由于Spring scheduling包并不是依赖quartz库，表达式虽然跟quartz类似，但部分quartz表达式在这里并不支持，例如每月最后一天 L，Spring Scheduled 不支持
         * >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
         */
        private String cron;
        /**
         * 分布式锁开关，默认：false
         */
        private boolean lockEnabled = false;
        /**
         * 分布式锁持续时间： 默认:1m
         */
        private Duration lockExpired = Duration.ofMinutes(1);
    }

}
